#include "triangle.h"

#include "math/ray.h"
#include "material/object_material.h"

#include <iostream> 
#include <limits>

namespace Sirat {

namespace Object {

Triangle::Triangle(Material::Object_Material* _object_material,const Math::Vector& _vertex1 ,const Math::Vector& _vertex2 ,const Math::Vector& _vertex3) : Object(_object_material) , cull_face(false) {
	vertices[0] = _vertex1;
	vertices[1] = _vertex2;
	vertices[2] = _vertex3;
}

Triangle::Triangle() : Object(), cull_face(false) { }
 
Triangle::Triangle(const Triangle& _triangle) : Object(_triangle) , cull_face(false) {
	vertices[0] = _triangle.vertices[0];
	vertices[1] = _triangle.vertices[1];
	vertices[2] = _triangle.vertices[2];
	
}
 
Triangle::~Triangle() { }

Math::Vector Triangle::get_vertex(unsigned short& _index) const {
	return vertices[_index];
}

Math::Vector Triangle::get_normal(const Math::Vector& _point) const {
	const Math::Vector v1 = vertices[1] - vertices[0];
	const Math::Vector v2 = vertices[2] - vertices[0];
	return ((v1.cross_product(v2)).normalize());
}

void Triangle::set_vertex(const Math::Vector& _vertex,unsigned short& _index) {
	vertices[_index] = _vertex;
}

tuple<double,Math::Vector,Object*> Triangle::intersection(const Math::Ray& _ray) { 
	// Tomas Möller and Ben Trumbore ray-triangle intersection
	std::numeric_limits<double> double_limit;
	const double infinity = double_limit.infinity();
// 	const double epsilon = double_limit.epsilon();
	const double epsilon = 0.000001;
	
	Math::Vector intersection_point;
	
	Math::Vector edge1 = vertices[1] - vertices[0];
	Math::Vector edge2 = vertices[2] - vertices[0];
	
	Math::Vector pvec = _ray.get_direction().cross_product(edge2);
	
	double det = edge1.dot_product(pvec);
//	if(get_normal(Math::Vector()).dot_product(_ray.get_direction()) <  0.0) return make_tuple(infinity,intersection_point);
	// Culling test
	if(cull_face) {
		if(det < epsilon) return make_tuple(infinity,intersection_point,this); 
		
		Math::Vector tvec = _ray.get_origin() - vertices[0];
		
		double u = tvec.dot_product(pvec);
		
		if(u < 0.0 || u > det) return make_tuple(infinity,intersection_point,this);
		
		Math::Vector qvec = tvec.cross_product(edge1);
		
		double v = _ray.get_direction().dot_product(qvec);
		
		if(v < 0.0 || u + v > det) return make_tuple(infinity,intersection_point,this); 
		
		double t = edge2.dot_product(qvec);
		
		double inv_det = 1.0 / det;
		
		t *= inv_det;
		u *= inv_det;
		v *= inv_det;
		
		intersection_point = vertices[0]*(1 - u - v) + vertices[1]*u + vertices[2]*v;
		return make_tuple(t,intersection_point,this);
	} else {
	if(det > -epsilon && det < epsilon) return make_tuple(infinity,intersection_point,this);
		double inv_det = 1.0 / det;
		
		Math::Vector tvec = _ray.get_origin() - vertices[0];
		
		double u = tvec.dot_product(pvec) * inv_det;
		
		if(u < 0.0 || u > 1.0) return make_tuple(infinity,intersection_point,this);
		
		Math::Vector qvec = tvec.cross_product(edge1);
		
		double v = _ray.get_direction().dot_product(qvec) * inv_det;
		
		if(v < 0.0 || u + v > 1.0) return make_tuple(infinity,intersection_point,this);
		
		double t = edge2.dot_product(qvec) * inv_det; 
		if(t < epsilon) return make_tuple(infinity,intersection_point,this);

		intersection_point = vertices[0]*(1.0 - u - v) + vertices[1]*u + vertices[2]*v;
		return make_tuple(t,intersection_point,this);
	}
}

tuple<double,double> Triangle::get_u_v(const Math::Vector& _point) const {
	double alfa = 0.0,beta = 0.0;
	
	double x = _point.x;
	double y = _point.y;
	double z = _point.z;
	
	double Xa = vertices[0].x;
	double Ya = vertices[0].y;
	double Za = vertices[0].z;
	
	double Xb = (vertices[1] - vertices[0]).x;
	double Yb = (vertices[1] - vertices[0]).y;
	double Zb = (vertices[1] - vertices[0]).z;
		
	double Xc = (vertices[2] - vertices[0]).x;
	double Yc = (vertices[2] - vertices[0]).y;
	double Zc = (vertices[2] - vertices[0]).z;
	
		
	alfa = -(-Xa*Yc + x*Yc + Xc*(Ya-y))/(Xc*Yb - Xb*Yc);
	beta =  (-Xa*Yb + x*Yb + Xb*(Ya-y))/(Xc*Yb - Xb*Yc);
	
// 	alfa = -(-Xa*Zc + x*Zc + Xc*(Za-z))/(Xc*Zb - Xb*Zc);
// 	beta = (-Xa*Zb + x*Zb + Xb*(Za-z))/(Xc*Zb - Xb*Zc);
// 	
// 	alfa = -(-Ya*Zc + y*Zc + Yc*(Za-z))/(Yc*Zb - Yb*Zc);
// 	beta = (-Ya*Zb + y*Zb + Yb*(Za-z))/(Yc*Zb - Yb*Zc);
	
	return make_tuple(alfa,beta);
}

Triangle& Triangle::operator=(const Triangle& _triangle) {
	Object::operator=(_triangle);
	vertices[0] = _triangle.vertices[0];
	vertices[1] = _triangle.vertices[1];
	vertices[2] = _triangle.vertices[2];
	return *this;
}

}

}
